import { Service } from 'egg';
import { Result } from '../util/result';
import MsgEnum from '../util/msgEnum';
import { Op } from 'sequelize';
import { v4 as uuidv4 } from 'uuid';
import { put } from '../util/ossFile';
import RoleEnum from '../util/roleEnum';
import areas from '../util/area';
import FileUtil from './../util/file';

export default class Product extends Service {
  public async deleteProducts(ids) {
    const { ctx } = this;
    let transaction;
    try {
      transaction = await ctx.model.transaction();

      let result;
      for await (const id of ids) {
        const product = await ctx.model.Product.findOne({
          where: {
            id,
          },
          attributes: ['uuid'],
          transaction
        });

        const { uuid } = product;

        const images = await ctx.model.Image.findAll({
          where: {
            fk_product: uuid
          },
          attributes: ['name']
        });

        if (images.length !== 0) {
          for await (const image of images) {
            const { name } = image;
            const imageName = name.split('=')[1];
            await FileUtil.deleteImage(imageName);
          }
        }


        await Promise.all([
          ctx.model.MergeProductPatent.destroy({
            where: { product_id: id },
            transaction,
          }),

          ctx.model.MergeProductOrganization.destroy({
            where: { product_id: id },
            transaction,
          }),

          ctx.model.Image.destroy({
            where: {
              fk_product: uuid
            }
          })
        ]);

        result = await ctx.model.Product.destroy({
          where: { id },
          transaction,
        });
      }

      await transaction.commit();
      return new Result({ result }, MsgEnum.UPLOAD_SUCCESS);
    } catch (e) {
      console.log(e);
      transaction.rollback();
      return new Result(null, MsgEnum.SERVER_ERROR);
    }
  }

  public async createProducts(datas, firstArray, username) {
    try {
      const msgArray: any[][] = [firstArray];
      for (const data of datas) {
        let transaction;
        try {
          transaction = await this.ctx.model.transaction();
          const hasProduct = await this.ctx.model.Product.findOne({
            where: { name: data[0], unit: data[1], model: data[2] },
          });
          if (hasProduct) {
            data[0] = `此产品名字${data[0]},所属单位${data[1]},成果型号${data[2]}重复请检查后重新输入`;
            msgArray.push(data);
            await transaction.rollback();
            continue;
          }
          const product = await this.ctx.model.Product.create(
            {
              name: data[0] ?? '',
              uuid: uuidv4(),
              unit: data[1] ?? '',
              model: data[2] ?? '',
              address: data[4] ?? '',
              registrationStatus: data[6] ?? '',
              contactPerson: data[8] ?? '',
              contactEmail: data[9] ?? '',
              contactTel: data[10] ?? '',
              description: data[11] ?? '',
              patent: data[5] ?? '',
              updateTime: new Date(),
              filler: username,
            },
            { transaction }
          );

          const applicationArea = await this.ctx.model.ApplicationArea.findOne({
            where: { name: data[7]?.split(',') ?? [] },
            transaction,
          });


          if (!applicationArea || applicationArea.length === 0) {
            data[7] = `应用领域${data[7]}找不到请重新输入`;
            msgArray.push(data);
            await transaction.rollback();
            continue;
          }
          await product.setApplication_area(applicationArea, { transaction });
          const orgArr = data[3]?.split(',');
          if(orgArr) {
            const organization = await this.ctx.model.Organization.findAll({
              where: { name: orgArr ?? [] },
              transaction,
            });


            if ((!organization || organization.length === 0) && orgArr.length != 0) {
              data[3] = `所属单位${data[3]}找不到请重新输入`;
              msgArray.push(data);
              await transaction.rollback();
              continue;
            }
            await product.setOrganizations(organization, { transaction });
          }
          await transaction.commit();
        } catch (e) {
          console.error(e);
          await transaction.rollback();
          return new Result({ upload: false, data: msgArray }, MsgEnum.SERVER_ERROR);

        }
      }
      if (msgArray.length === 1) {
        return new Result({ upload: true }, MsgEnum.UPLOAD_SUCCESS);
      }

      return new Result(
        { upload: false, data: msgArray },
        MsgEnum.FILE_UPLOAD_ERROR
      );
    } catch (e) {
      console.log(e);
      return new Result(null, MsgEnum.SERVER_ERROR);
    }
  }

  public async createProduct(
    productNo,
    productId,
    name,
    registrationStatus,
    model,
    application_area,
    organization,
    address,
    patent,
    contactPerson,
    contactEmail,
    contactTel,
    description,
    updateTime,
    filler,
    company,
    pictureUrls
  ) {
    const { ctx } = this;

    const result = await ctx.model.transaction(async t => {
      let product;
      if (productId) {
        product = await ctx.model.Product.findOne({
          where: {
            id: productId
          }
        });
        await product.update(
          {
            name,
            registrationStatus,
            model,
            productNo,
            contactPerson,
            contactEmail,
            contactTel,
            description,
            patent,
            updateTime: new Date(updateTime),
            filler,
            unit: company,
            address
          },
          {
            where: {
              id: productId,
            }
          },
          { transaction: t }
        );
      } else {
        product = await this.ctx.model.Product.create({
          name,
          uuid: uuidv4(),
          registrationStatus,
          model,
          productNo,
          contactPerson,
          contactEmail,
          contactTel,
          description,
          patent,
          updateTime: new Date(updateTime),
          filler,
          unit: company,
          address
        },
        { transaction: t });
      }

      console.log(product);

      if (!product) {
        return false;
      }

      if (application_area) {
        const applicationArea = await this.ctx.model.ApplicationArea.findOne({
          where: { uuid: application_area.split(',') },
        }, { transaction: t });
        await product.setApplication_area(applicationArea, { transaction: t });
      }

      if (organization) {
        // const organizations = await this.ctx.model.Organization.findAll({
        //   where: { uuid: organization.split(',') },
        // }, { transaction: t });
        // await product.setOrganizations(organizations, { transaction: t });
        for await (const organizationUuid of organization) {
          const org = await ctx.model.Organization.findOne({
            where: { uuid: organizationUuid },
          }, { transaction: t });
          await product.setOrganizations(org, { transaction: t });
        }
      }

      // if (patent) {
      //   const patents = await this.ctx.model.Patent.findAll({
      //     where: { uuid: patent.split(',') },
      //   }, { transaction: t });

      //   await product.setPatents(patents, { transaction: t });
      // }

      const createResult: any = [];
      for (const pictureUrl of pictureUrls) {
        createResult.push(
          ctx.model.Image.create({
            uuid: uuidv4(),
            name: pictureUrl,
            fkProduct: product.uuid
          }, { transaction: t })
        );
      }

      await Promise.all(createResult);
      return true;
    });
    return result;
  }

  public async createAll(data, r) {
    let transaction;
    try {
      transaction = await this.ctx.model.transaction();

      const product = await this.ctx.model.Product.create(
        {
          name: data.name,
          uuid: uuidv4(),
          registrationStatus: data.registrationStatus,
          model: data.model,
          productNo: data.productNo,
          contactPerson: data.contactPerson,
          contactEmail: data.contactEmail,
          contactTel: data.contactTel,
          description: data.description,
          updateTime: new Date(data.updateTime),
          filler: data.filler,
          unit: data.company,
          address: data.address,

        },
        { transaction }
      );

      if (data.application_area) {
        const applicationArea = await this.ctx.model.ApplicationArea.findOne({
          where: { uuid: data.application_area.split(',') },
          transaction,
        });
        await product.setApplication_area(applicationArea, { transaction });
      }
      if (data.organization) {
        const organization = await this.ctx.model.Organization.findAll({
          where: { uuid: data.organization.split(',') },
          transaction,
        });
        await product.setOrganizations(organization, { transaction });
      }
      if (data.patent) {
        const patent = await this.ctx.model.Patent.findAll({
          where: { uuid: data.patent.split(',') },
          transaction,
        });

        await product.setPatents(patent, { transaction });
      }
      await transaction.commit();
      const res = await put(RoleEnum.PRODUCT, product.id, r);
      if (res.error) {
        return new Result(null, MsgEnum.SERVER_ERROR);
      }
      return new Result({ upload: true }, MsgEnum.UPLOAD_SUCCESS);
    } catch (e) {
      console.log(e);
      await transaction.rollback();
      return new Result(null, MsgEnum.SERVER_ERROR);
    }
  }

  public async uploadAll(id: number, data, r) {
    let transaction;
    try {
      let res = { error: false };
      if (r.file_name !== 'null.xlsx') {
        res = await put(RoleEnum.PRODUCT, id, r);
        if (res.error) {
          return new Result(null, MsgEnum.SERVER_ERROR);
        }
      }
      transaction = await this.ctx.model.transaction();
      const product = await this.ctx.model.Product.findOne({
        where: {
          id,
        },
        transaction,
      });
      if (product.productNo !== data.productNo) {
        const hasProduct = await this.ctx.model.Product.findOne({
          where: { productNo: data.productNo },
        });
        if (hasProduct) {
          transaction.rollback();
          return new Result(null, MsgEnum.PROJUCT_REPEAT);
        }
      }
      await product.update(
        {
          name: data.name,
          registrationStatus: data.registrationStatus,
          model: data.model,
          productNo: data.productNo,
          contactPerson: data.contactPerson,
          contactEmail: data.contactEmail,
          contactTel: data.contactTel,
          description: data.description,
          updateTime: new Date(data.updateTime),
          filler: data.filler,
          unit: data.company,
          address: data.address
        },
        { transaction }
      );
      if (data.application_area) {
        const applicationArea = await this.ctx.model.ApplicationArea.findOne({
          where: { uuid: data.application_area.split(',') },
          transaction,
        });
        await product.setApplication_area(applicationArea, { transaction });
      }
      if (data.organization) {
        const organization = await this.ctx.model.Organization.findAll({
          where: { uuid: data.organization.split(',') },
          transaction,
        });
        await product.setOrganizations(organization, { transaction });
      }
      if (data.patent) {
        const patent = await this.ctx.model.Patent.findAll({
          where: { uuid: data.patent.split(',') },
          transaction,
        });

        await product.setPatents(patent, { transaction });
      }
      await transaction.commit();
      return new Result({ upload: true, file: res }, MsgEnum.UPLOAD_SUCCESS);
    } catch (e) {
      console.log(e);
      await transaction.rollback();
      return new Result(null, MsgEnum.SERVER_ERROR);
    }
  }

  public async getRandomColor() {
    return (
      '#' +
      (function get(color): any {
        return (color += '0123456789abcdef'[Math.floor(Math.random() * 16)]) &&
        color.length === 6
          ? color
          : get(color);
      })('')
    );
  }

  public async getStatus() {

    const mainData = await this.ctx.model.Product.findAll({
      attributes: ['registrationStatus'], include: [{
        model: this.ctx.model.ApplicationArea,
        required: true,
        attributes: ['name']
      }]
    });
    const datas: any = {};
    mainData.forEach((v: any) => {
      const name = v.application_area.name;
      if (!datas.hasOwnProperty(name)) {
        datas[name] = 0;
      } else {
        datas[name]++;
      }
    });
    const dataAxis: any = [];
    const data: any = [];
    for (const name in datas) {
      if (datas.hasOwnProperty(name)) {
        dataAxis.push(name);
        data.push({ value: datas[name],
          itemStyle: { color: await this.getRandomColor() },
        });
      }
    }
    return { data, dataAxis };
  }

  public async getAllPlace() {
    const mainData = await this.ctx.model.Product.findAll({
      attributes: ['registrationStatus'], include: [{
        model: this.ctx.model.ApplicationArea,
        required: true,
        attributes: ['name']
      }]
    });

    const registrationStatuss: any = { 已注册: 0, 未注册: 0 };
    const dataw: any = [];
    mainData.forEach((v: { address: string; registrationStatus: string }) => {
      registrationStatuss[v.registrationStatus]++;
    });
    for (const name in registrationStatuss) {
      if (registrationStatuss.hasOwnProperty(name)) {
        dataw.push({ type: name, value: registrationStatuss[name] });
      }
    }
    return dataw;
  }

  public async getProductMaps() {
    const mainData = await this.ctx.model.Product.findAll({ attributes: ['address'] });
    const data: any = {};
    mainData.forEach((v: { address: string }) => {
      let add;
      if(v.address?.split(';')[0] === '台湾省') {
        add = '台湾';
      }else {
        add = v.address?.split(';')[1];
      }
      if (!data.hasOwnProperty(add)) {
        data[add] = 1;
      } else {
        data[add]++;
      }
    });
    let dataw = 'lng,lat,name,v\n';
    for (const name in data) {
      if (data.hasOwnProperty(name)) {

        if(areas[name] && name !== undefined && name !== 'undefined') {
          dataw += (areas[name]?.join(',') ?? '0,0') + ',' + name + ',' + data[name] + '\n';
        }else {
          dataw += '\n';
        }
      }
    }
    return dataw;
  }

  public async getProjectList(
    page: number,
    name: string,
    fk_applicationArea: string,
    date: string,
    keyword: string
  ) {
    let transaction;
    try {
      transaction = await this.ctx.model.transaction();
      let where;
      if (date) {

        where = {
          name: { [Op.like]: `%${name}%` },
          unit: { [Op.like]: `%${keyword}%` },
          updateTime: new Date(date),
        };
      } else {
        where = {
          name: { [Op.like]: `%${name}%` },
          unit: { [Op.like]: `%${keyword}%` },
        };
      }

      const results = await this.ctx.model.Product.findAndCountAll({
        where,
        transaction,
        distinct: true,
        include: [
          {
            model: this.ctx.model.ApplicationArea,
            required: true,
            where: {
              name: { [Op.like]: `%${fk_applicationArea}%` },
            },
          },
          {
            model: this.ctx.model.Organization,
            required: false,
            // where: {
            //   name: { [Op.like]: `%${keyword}%` },
            // },
          },
          {
            model: this.ctx.model.Image,
            attributes: ['name'],
          }
        ],
        offset: Number((page - 1) * 10),
        limit: 10,
      });
      const patents = await this.ctx.model.Patent.findAll({ transaction });
      const organizations = await this.ctx.model.Organization.findAll({
        transaction,
      });
      const application_area = await this.ctx.model.ApplicationArea.findAll({
        transaction,
      });
      await transaction.commit();
      return new Result({
        rows: results.rows,
        count: results.count,
        patents,
        organizations,
        application_area,
      });
    } catch (e) {
      await transaction.rollback();
      return new Result(null, MsgEnum.SERVER_ERROR);
    }
  }

  async testProductExist(name: any, model: any, unit: any) {
    let transaction;
    try {
      transaction = await this.ctx.model.transaction();
      const results = await this.ctx.model.Product.findOne({
        where: { name, model, unit },
        transaction,
        distinct: true,
      });
      await transaction.commit();

      if (!results) {

        return new Result({ find: true }, MsgEnum.UPLOAD_SUCCESS);

      }
      return new Result(null, MsgEnum.HAS_FIND_PRODUCT);


    } catch (e) {
      await transaction.rollback();
      return new Result(null, MsgEnum.SERVER_ERROR);

    }
  }

  async getProductDetail(patientUuid: string) {
    const { ctx } = this;

    const productDetail = await ctx.model.Product.findOne({
      where: {
        uuid: patientUuid,
      },
      attributes: ['name', 'unit', 'description', 'uuid'],
      include: [
        {
          model: ctx.model.ApplicationArea,
          attributes: ['name']
        },
        {
          model: ctx.model.Image,
          attributes: ['name']
        }
      ],
    });

    if (productDetail) {
      const { uuid, name, unit, description, application_area, Images } = productDetail;
      const { name: applicationArea } = application_area;
      const imageList: any = [];
      if (Images.length !== 0) {
        for (const image of Images) {
          const { name } = image;
          imageList.push(name);
        }
      }
      return {
        uuid,
        name,
        unit,
        description,
        applicationArea,
        imageList
      };
    }
    return false;
  }

  async deleteImage (databaseName: string) {
    const { ctx } = this;

    const result = await ctx.model.transaction(async t => {
      const deleteImage = await ctx.model.Image.destroy({
        where: {
          name: databaseName,
        },
      }, { transaction: t });

      return deleteImage;
    });

    return result;
  }
}
